package com.bolt.support.base.service;

import com.bolt.common.convert.Convert;
import com.bolt.common.reflect.ReflectionUtil;
import com.bolt.support.spring.jpa.entity.BaseNestedSetEntity;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.Query;
import java.io.Serializable;
import java.util.List;

/**
 * Created by Administrator on 2021/2/24.
 */
public abstract class BaseNestedSetService<T extends BaseNestedSetEntity<ID,T>, ID extends Serializable> extends BaseService <T,ID> {

    private T queryEntity(ID id) {
        Query query = entityManager.createQuery("from " + entityName + " where id=:id");
        query.setParameter("id", id);
        return (T) query.getSingleResult();
    }


    private void moveToRight(T entity, int newRight) {
        //记录移动对象的左右值
        Integer left = entity.getLft();
        Integer right = entity.getRgt();
        int width = entity.getRgt() - entity.getLft() + 1;
        //"临时"移除节点，把移动节点及子节点，全部置为负数
        Query update = entityManager.createQuery("update " + entityName + " set rgt = 0-rgt, lft=0-lft where lft >= :curLft and rgt <= :curRgt");
        update.setParameter("curLft", left);
        update.setParameter("curRgt", right);
        int cnt = update.executeUpdate();

        //移动区间节点调整
        Query update1 = entityManager.createQuery("update " + entityName + " set rgt = rgt - :width where rgt > :curRight and rgt < :newRight");
        Query update2 = entityManager.createQuery("update " + entityName + " set lft = lft - :width where lft > :curRight and lft < :newRight");
        update1.setParameter("curRight", right);
        update2.setParameter("curRight", right);
        update1.setParameter("newRight", newRight);
        update2.setParameter("newRight", newRight);
        update1.setParameter("width", width);
        update2.setParameter("width", width);
        int cnt1 = update1.executeUpdate();
        int cnt2 = update2.executeUpdate();

        //调整移动节点及子节点
        int diff = newRight - 1 - entity.getRgt();
        entity.setLft(left + diff);
        entity.setRgt(right + diff);

        //批量移动子节点
        if (entity.hasChildren()) {
            Query update3 = entityManager.createQuery("update " + entityName + " set rgt = rgt - :diff, lft=lft - :diff where lft < 0 and rgt < 0");
            update3.setParameter("diff", diff);
            int cnt3 = update3.executeUpdate();

            //恢复移动节点及子节点
            Query update4 = entityManager.createQuery("update " + entityName + " set rgt = 0-rgt, lft=0-lft where lft < 0 and rgt < 0");
            int cnt4 = update4.executeUpdate();
        }
    }

    private void moveToLeft(T entity, int newLeft) {
        //记录移动对象的左右值
        Integer left = entity.getLft();
        Integer right = entity.getRgt();
        int width = entity.getRgt() - entity.getLft() + 1;

        //"临时"移除节点，把移动节点及子节点，全部置为负数
        Query update = entityManager.createQuery("update " + entityName + " set rgt = 0-rgt, lft=0-lft where lft >= :curLft and rgt <= :curRgt");
        update.setParameter("curLft", left);
        update.setParameter("curRgt", right);
        update.executeUpdate();

        // 移动区间节点调整
        Query update1 = entityManager.createQuery("update " + entityName + " set rgt = rgt + :width where rgt >= :newLeft and rgt < :curRight");
        Query update2 = entityManager.createQuery("update " + entityName + " set lft = lft + :width where lft >= :newLeft and lft < :curRight");
        update1.setParameter("curRight", right);
        update2.setParameter("curRight", right);
        update1.setParameter("newLeft", newLeft);
        update2.setParameter("newLeft", newLeft);
        update1.setParameter("width", width);
        update2.setParameter("width", width);
        update1.executeUpdate();
        update2.executeUpdate();

        //调整移动节点及子节点
        int diff = left - newLeft;
        entity.setLft(left - diff);
        entity.setRgt(right - diff);

        //批量移动子节点
        if (entity.hasChildren()) {
            Query update3 = entityManager.createQuery("update " + entityName + " set rgt = rgt + :diff, lft=lft + :diff where lft < 0 and rgt < 0");
            update3.setParameter("diff", diff);
            update3.executeUpdate();

            //恢复移动节点及子节点
            Query update4 = entityManager.createQuery("update " + entityName + " set rgt = 0-rgt, lft=0-lft where lft < 0 and rgt < 0");
            update4.executeUpdate();
        }
    }


    @Transactional(rollbackFor = Exception.class)
    public T save(T entity) {
        //非根节点处理
        if (entity.getParent() != null && entity.getParent().getId() != null) {
            //清除缓存
            entityManager.getEntityManagerFactory().getCache().evict(entityClass);
            entityManager.flush();
            entityManager.clear();

            ID parentId = entity.getParent().getId();
            //新增节点处理
            if (entity.isNew()) {
                T parent = queryEntity(parentId);

                //LAST位置或空白节点追加新增节点
                entity.setLft(parent.getLft() + 1);
                entity.setRgt(parent.getLft() + 2);
                entity.setDepth(parent.getDepth() + 1);
                entity.setParent(parent);

                //更新待插入新位置之前的树结构数据
                Query update1 = entityManager.createQuery("update " + entityName + " set rgt = rgt+2 where rgt >= :newLft");
                Query update2 = entityManager.createQuery("update " + entityName + " set lft = lft+2 where lft > :newLft");
                update1.setParameter("newLft", parent.getLft());
                update2.setParameter("newLft", parent.getLft());
                update1.executeUpdate();
                update2.executeUpdate();
                getRepository().save(entity);
            } else {
                T old = queryEntity(entity.getId());
                Integer right = old.getRgt();
                if (entity.getParent().getId().equals(old.getParent().getId())) {
                    this.update(entity, old, "lft", "rgt", "depth", "parent");
                } else {
                    T newParent = queryEntity(entity.getParent().getId());
                    Integer newParentRgt = newParent.getRgt();
                    entity.setLft(old.getLft());
                    entity.setRgt(old.getRgt());
                    if (newParentRgt > right) { //向右移动
                        moveToRight(entity, newParentRgt);
                    } else { //向左移动
                        moveToLeft(entity, newParentRgt);
                    }
                    //设置新的父节点对象关联
                    entity.setParent(newParent);
                    entity.setDepth(newParent.getDepth() + 1);
                    getRepository().save(entity);
                }
            }

        } else {
            //根节点处理
            entity.setLft(1);
            entity.setRgt(2);
            entity.setDepth(0);
            entity.setParent(null);
            getRepository().save(entity);
        }


        return entity;
    }

    /**
     * 查询所有父级节点
     *
     * @return
     */
    public List <T> findRoot() {
        Query query = entityManager.createQuery("FROM " + entityName + " WHERE parent is null order by orderRank");
        return (List <T>) query.getResultList();
    }

    /**
     * 查询相关父级的每一层子级
     *
     * @param node
     * @return
     */
    public List <T> findChildren(T node) {
        Query query = entityManager.createQuery("FROM " + entityName + " WHERE parent.id = :parentId order by orderRank");
        query.setParameter("parentId", node.getId());
        return query.getResultList();
    }

    public List <T> findChildren(Serializable parentId) {
        Query query = entityManager.createQuery("FROM " + entityName + " WHERE parent.id = :parentId order by orderRank");
        query.setParameter("parentId", parentId);
        return query.getResultList();
    }

    public List <T> findAllChildren(T node) {
        Query query = entityManager.createQuery("SELECT node FROM " + entityName + " node, " + entityName + " parent  WHERE   node.lft BETWEEN parent.lft and parent.rgt and parent.id=:parentId ORDER BY  node.lft");
        query.setParameter("parentId", node.getId());
        return query.getResultList();
    }

    public List<T>  findAllChildren(List<T> menuList, List<T> allMenus) {
        for (T treeData : menuList) {
            allMenus.add(treeData);
            List<T> menus = findChildren(treeData.getId());
            if(menus!=null && menus.size()!=0){
                findAllChildren(menus, allMenus);
            }
        }
        return allMenus;
    }



    @Transactional(rollbackFor = Exception.class)
    public void delete(T entity) {
        if (!entity.hasChildren()) {
            //记录删除对象的左右值
            Integer left = entity.getLft();
            Integer right = entity.getRgt();

            //先删除当前对象
            getRepository().delete(entity);
            //删除节点后，移动右侧节点。注意此算法需要确保已递归删除所有子节点，如果出现孤儿节点情况则会有问题。
            int width = right - left + 1;
            Query update1 = entityManager.createQuery("update " + entityName + " set rgt = rgt - :width where rgt > :rightParam");
            Query update2 = entityManager.createQuery("update " + entityName + " set lft = lft - :width where lft > :rightParam");
            update1.setParameter("rightParam", right);
            update2.setParameter("rightParam", right);
            update1.setParameter("width", width);
            update2.setParameter("width", width);
            update1.executeUpdate();
            update2.executeUpdate();
        }
    }


    protected String nextCode(T entity, String fieldName) {
        int maxId = 0;
        if (null != entity.getParent()) {

            maxId = Convert.toInt(entityManager.createQuery("select max(" + fieldName + ") from " + entityName + " where parent=:parent")
                    .setParameter("parent", entity.getParent()).getSingleResult(), 0);
        } else {
            maxId = Convert.toInt(entityManager.createQuery("select max(" + fieldName + ") from " + entityName + " where parent  IS NULL")
                    .getSingleResult(), 0);
        }
        String newId = null;
        if (maxId == 0 && null == entity.getParent()) {
            newId = "10";
            return newId;
        }
        if (maxId == 0 && null != entity.getParent()) {
            T parent = queryEntity(entity.getParent().getId());
            String parentCode = (String) ReflectionUtil.getFieldValue(parent, fieldName);
            newId = parentCode + "01";
            return newId;
        }
        if (maxId != 0 && null != entity.getParent()) {
            newId = String.valueOf(maxId + 1);
            return newId;
        }

        int numId = Convert.toInt(newId, 0) + 1;
        String tempId = String.valueOf(numId);
        if (tempId.length() == 1) {
            tempId = "0" + tempId;
        }
        String childrenCode = (String) ReflectionUtil.getFieldValue(entity, fieldName);
        newId = childrenCode + tempId;
        return newId;

    }

}

 